package com.hyc.www.service.impl;

import com.hyc.www.dao.OrderDao;
import com.hyc.www.dao.RecordDao;
import com.hyc.www.dao.TicketDao;
import com.hyc.www.dao.UserDao;
import com.hyc.www.exception.DaoException;
import com.hyc.www.factory.DaoProxyFactory;
import com.hyc.www.model.dto.ServiceResult;
import com.hyc.www.model.po.Order;
import com.hyc.www.model.po.Record;
import com.hyc.www.model.po.Ticket;
import com.hyc.www.model.po.User;
import com.hyc.www.model.vo.OrderVO;
import com.hyc.www.service.TicketService;
import com.hyc.www.service.constants.ServiceMessage;
import com.hyc.www.service.constants.Status;

import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

import static com.hyc.www.util.UUIDUtils.getUUID;

/**
 * @author <a href="mailto:kobe524348@gmail.com">黄钰朝</a>
 * @description 用于提供车票相关的服务
 * @date 2019-07-17 17:07
 */
public class TicketServiceImpl implements TicketService {
    private static final TicketDao TICKET_DAO = (TicketDao) DaoProxyFactory.getInstance().getProxyInstance(TicketDao.class);
    private static final UserDao USER_DAO = (UserDao) DaoProxyFactory.getInstance().getProxyInstance(UserDao.class);
    private static final OrderDao ORDER_DAO = (OrderDao) DaoProxyFactory.getInstance().getProxyInstance(OrderDao.class);
    private static final RecordDao RECORD_DAO = (RecordDao) DaoProxyFactory.getInstance().getProxyInstance(RecordDao.class);

    /**
     * 一次放出的车票数
     */
    private static final int TICKET_NUMBER = 1;

    /**
     * 一次放出车票后参与抢票的用户数
     */
    private static final int COUNT_NUMBER = TICKET_NUMBER * 9;
    private static final int COUNT_NUMBER_VIP = TICKET_NUMBER * 3;
    private static final int COUNT_NUMBER_SVIP = TICKET_NUMBER;

    private static final CyclicBarrier BARRIER = new CyclicBarrier(COUNT_NUMBER);
    private static final CyclicBarrier BARRIER_VIP = new CyclicBarrier(COUNT_NUMBER_VIP);
    private static final CyclicBarrier BARRIER_SVIP = new CyclicBarrier(COUNT_NUMBER_SVIP);

    private static final Map<BigInteger, TicketList> TICKET_LIST_MAP = new ConcurrentHashMap<>();
    private static final Map<BigInteger, TicketList> TICKET_LIST_MAP_VIP = new ConcurrentHashMap<>();
    private static final Map<BigInteger, TicketList> TICKET_LIST_MAP_SVIP = new ConcurrentHashMap<>();

    private static final ReentrantLock REENTRANT_LOCK = new ReentrantLock();
    private static final Map<BigInteger, Order> ORDER_MAP = new ConcurrentHashMap<>();
    private static final TicketDistributorImpl DISTRIBUTOR = TicketDistributorImpl.getInstance();
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(30, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(COUNT_NUMBER), Executors.defaultThreadFactory());

    @Override
    public ServiceResult buyTicket(BigInteger ticketId, BigInteger userId) {

        User user = USER_DAO.getUserById(userId);
        TicketList ticketList;

        //记录抢票请求
        String uuid = getUUID();
        Record record = new Record();
        record.setTicketId(ticketId);
        record.setUserId(userId);
        record.setUuid(uuid);
        RECORD_DAO.insert(record);
        record= RECORD_DAO.getRecordByUUID(uuid);

        //分不同等级vip进行抢票环境设定
        Map<BigInteger, TicketList> map;
        CyclicBarrier barrier;
        if ("VIP".equalsIgnoreCase(user.getType())) {
            map = TICKET_LIST_MAP_VIP;
            barrier = BARRIER_VIP;
        } else if ("SVIP".equalsIgnoreCase(user.getType())) {
            map = TICKET_LIST_MAP_SVIP;
            barrier = BARRIER_SVIP;
        } else {
            map = TICKET_LIST_MAP;
            barrier = BARRIER;
        }

        //独占锁进行取票动作
        REENTRANT_LOCK.lock();
        try {
            ticketList = map.get(ticketId);
            if (ticketList == null || (ticketList.isFull())) {
                if (ticketList != null) {
                    map.remove(ticketId);
                }
                //取出车票
                Queue<Order> availableTickets = DISTRIBUTOR.getAvailableTickets(ticketId, TICKET_NUMBER);
                barrier.reset();
                ticketList = new TicketList(availableTickets, barrier);
                map.put(ticketId, ticketList);
            }

            FutureTask<Order> futureTask = new FutureTask<>(new BuyTicketThread(user, ticketId, ticketList,record));
            Thread thread = new Thread(futureTask);
            EXECUTOR.execute(thread);
        } catch (Exception e) {
            record.setResult("抢票失败");
            e.printStackTrace();
        } finally {
            RECORD_DAO.update(record);
            REENTRANT_LOCK.unlock();
        }
        return null;
    }


    class BuyTicketThread implements Callable {

        private final CyclicBarrier barrier;
        private final BigInteger userId;
        private final User user;
        private final BigInteger ticketId;
        private final Queue<Order> tickets;
        private final TicketList ticketList;
        private final Record record;
        private final OrderDao orderDao = (OrderDao) DaoProxyFactory.getInstance().getProxyInstance(OrderDao.class);

        BuyTicketThread(User user, BigInteger ticketId, TicketList ticketList,Record record) {
            this.userId = user.getId();
            this.user = user;
            this.record = record;
            this.barrier = ticketList.getBarrier();
            this.tickets = ticketList.getQueue();
            this.ticketList = ticketList;
            this.ticketId = ticketId;
        }

        @Override
        public Object call() throws Exception {
            Order order;
            try {

                try {
                    barrier.await(5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    System.out.println("等待超时,线程屏障开放");
                } catch (BrokenBarrierException e){
                }
                ticketList.setFull(true);
                order = tickets.remove();
                if (order != null) {
                    order.setUserId(userId);
                    if (orderDao.insert(order) == 1) {
                        record.setResult("抢票成功");
                        System.out.println("用户[id= " + userId + " level=" + user.getType() + "] 抢到一张票:" + order.getNumber());
                    } else {
                        record.setResult("抢票失败");
                        System.out.println("用户[id= " + userId + " level=" + user.getType() + "] 没有抢到票");
                    }
                } else {
                    record.setResult("抢票失败");
                    System.out.println("用户[id= " + userId + " level=" + user.getType() + "] 没有抢到票");

                }
            } catch (NoSuchElementException e) {
                record.setResult("抢票失败");
                System.out.println("用户[id= " + userId + " level=" + user.getType() + "] 没有抢到票");

            } finally {
                //清空抢票状态，更新抢票记录
                ticketList.setFull(true);
                RECORD_DAO.update(record);
            }
            return null;
        }
    }

    /**
     * 退票
     *
     * @notice none
     * @author <a href="mailto:kobe524348@gmail.com">黄钰朝</a>
     * @date 2019-07-17
     */
    @Override
    public ServiceResult cancelOrder(BigInteger orderId) {
        if (orderId == null) {
            return new ServiceResult(Status.ERROR, ServiceMessage.PARAMETER_NOT_ENOUGHT.message, null);
        }

        try {
            Order order = ORDER_DAO.getOrderById(orderId);
            //返回余票
            Ticket ticket = TICKET_DAO.getTicketById(order.getTicketId());
            ticket.setInventory(ticket.getInventory() + 1);
            TICKET_DAO.update(ticket);
            if (ORDER_DAO.delete(order) != 1) {
                return new ServiceResult(Status.ERROR, ServiceMessage.PLEASE_REDO.message, null);
            }
        } catch (DaoException e) {
            e.printStackTrace();
            return new ServiceResult(Status.ERROR, ServiceMessage.DATABASE_ERROR.message, null);
        }
        return new ServiceResult(Status.SUCCESS, null, null);
    }

    /**
     * 查询符合条件的车票
     *
     * @param ticket 车票的限定条件
     * @param page   结果的页数
     * @return 返回车票信息
     * @name listTicket
     * @notice none
     * @author <a href="mailto:kobe524348@gmail.com">黄钰朝</a>
     * @date 2019-07-17
     */
    @Override
    public ServiceResult listTicket(Ticket ticket, int page) {
        if (ticket == null) {
            return new ServiceResult(Status.ERROR, ServiceMessage.PARAMETER_NOT_ENOUGHT.message, null);
        }
        List ticketList;
        String[] selectField = {"number", "origin", "destination", "depart", "arrive",
                "price", "inventory", "id", "status", "gmt_create", "gmt_modified"};
        ticket.setOrigin(ticket.getOrigin() == null | ticket.getOrigin().trim().isEmpty() ? null : ticket.getOrigin());
        ticket.setDestination(ticket.getDestination() == null || ticket.getDestination().trim().isEmpty() ? null : ticket.getDestination());
        ticket.setNumber(ticket.getNumber() == null || ticket.getNumber().trim().isEmpty() ? null : ticket.getNumber());

        try {
            ticketList = TICKET_DAO.listTicket(selectField, ticket);
            if (ticketList.size() == 0) {
                return new ServiceResult(Status.ERROR, ServiceMessage.NO_MORE.message, ticketList);
            }
        } catch (DaoException e) {
            e.printStackTrace();
            return new ServiceResult(Status.ERROR, ServiceMessage.DATABASE_ERROR.message, ticket);
        }
        return new ServiceResult(Status.SUCCESS, null, ticketList);
    }

    /**
     * 查询某个用户的车票
     *
     * @param userId
     * @return 返回车票信息
     * @name listTicketOrder
     * @notice none
     * @author <a href="mailto:kobe524348@gmail.com">黄钰朝</a>
     * @date 2019-07-17
     */
    @Override
    public ServiceResult listTicketOrder(BigInteger userId) {
        if (userId == null) {
            return new ServiceResult(Status.ERROR, ServiceMessage.PARAMETER_NOT_ENOUGHT.message, null);
        }
        List<Order> orderList;
        List<OrderVO> orderVOList = new LinkedList<>();
        try {
            orderList = ORDER_DAO.listOrderByUid(userId);
            if (orderList.size() == 0) {
                return new ServiceResult(Status.ERROR, ServiceMessage.NO_MORE.message, orderList);
            }
            for (int i = 0; i < orderList.size(); i++) {
                OrderVO orderVO = new OrderVO();
                Order order = orderList.get(i);
                Ticket ticket = TICKET_DAO.getTicketById(order.getTicketId());
                orderVO.setId(order.getId());
                orderVO.setUserId(userId);
                orderVO.setOrderNumber(orderList.get(i).getNumber());
                orderVO.setUserId(orderList.get(i).getTicketId());
                orderVO.setOrigin(ticket.getOrigin());
                orderVO.setDestination(ticket.getDestination());
                orderVO.setDepart(ticket.getDepart());
                orderVO.setArrive(ticket.getArrive());
                orderVO.setNumber(ticket.getNumber());
                orderVO.setPrice(ticket.getPrice());
                orderVOList.add(orderVO);
            }
        } catch (DaoException e) {
            e.printStackTrace();
            return new ServiceResult(Status.ERROR, ServiceMessage.DATABASE_ERROR.message, null);
        }
        return new ServiceResult(Status.SUCCESS, null, orderVOList);
    }

    /**
     * 查询某个用户的车票
     *
     * @param userId
     * @return 返回记录
     * @name listTicketOrder
     * @notice none
     * @author <a href="mailto:kobe524348@gmail.com">黄钰朝</a>
     * @date 2019-07-17
     */
    @Override
    public ServiceResult listTicketRecord(BigInteger userId) {
        if (userId == null) {
            return new ServiceResult(Status.ERROR, ServiceMessage.PARAMETER_NOT_ENOUGHT.message, null);
        }
        List<Record> recordList;
        try {
            recordList = RECORD_DAO.listRecordByUid(userId);
            if (recordList.size() == 0) {
                return new ServiceResult(Status.ERROR, ServiceMessage.NO_MORE.message, recordList);
            }
        } catch (DaoException e) {
            e.printStackTrace();
            return new ServiceResult(Status.ERROR, ServiceMessage.DATABASE_ERROR.message, null);
        }
        return new ServiceResult(Status.SUCCESS, null, recordList);
    }


}
